#include "v_cpu.h"
#include "memory_avm.h"
#include <stdio.h>
#include <memory.h>

// Test instruction: mov eax, [0x12345678]
/*static bit8u test_instr0[] = { 0xA1, 0x78, 0x56, 0x34, 0x12 };
static bit8u test_instr1[] = { 0x8B, 0xC3 };
static bit8u test_instr2[] = { 0x8B, 0x58, 0x12 };
static bit8u test_instr3[] = { 0x8B, 0x5C, 0x34, 0x12 };
static bit8u test_instr4[] = { 0x8B, 0x5C, 0x85, 0x12 };
static bit8u test_instr5[] = { 0x8B, 0x98, 0x78, 0x56, 0x34, 0x12 };
static bit8u test_instr6[] = { 0x8B, 0x9C, 0x43, 0x78, 0x56, 0x34, 0x12 };
static bit8u test_instr7[] = { 0x8B, 0x9C, 0x85, 0x78, 0x56, 0x34, 0x12 };
static bit8u test_instr8[] = { 0x8B, 0x18 };
static bit8u test_instr9[] = { 0x8B, 0x1D, 0x78, 0x56, 0x34, 0x12 };
static bit8u test_instr10[] = { 0x8B, 0x9C, 0x43, 0x78, 0x56, 0x34, 0x12 };
static bit8u test_instr11[] = { 0x8B, 0x1C, 0x5D, 0x78, 0x56, 0x34, 0x12 };

static bit8u test_instr12[] = { 0x8B, 0x1D, 0x34, 0x12 };*/
static bit8u test_instr13[] = { 0x04, 0x12 };
/*
static const bit8u *test_instr[] = { test_instr0, test_instr1, test_instr2, 
								test_instr3, test_instr4, test_instr5,
								test_instr6, test_instr7, test_instr8,
								test_instr9, test_instr10, test_instr11 };
*/
static bit8u test_aaa[] = { 0x37, 0x00 };
#define PAGE_SIZE 1024
void paging_test();

avm_memory avm_mem(0x100000);
v_cpu v_cpu0;

int main() {
	instruction instr;
	instr.set_as32(1);
	int length = 0;

	//for (int i = 0; i < 12; i++) {
	//	printf("/*******************%d*********************/\n", i);
	//	length = v_cpu0.fetch_decode(test_instr[i], &instr, 15);
	//	printf("The instruction length is: %d.\n", length);
	//	printf("/*****************************************/\n");
	//	printf("\n");

	//}

	//instr.set_as32(0);
	//length = v_cpu0.fetch_decode(test_instr12, &instr, 15);
	//printf("The instruction length is: %d.\n", length);
	
	instr.set_as32(0);
	length = v_cpu0.fetch_decode(test_instr13, &instr, 15);
	printf("The instruction length is: %d.\n", length);
	//v_cpu0.get_snapshot();
	printf("test handler: %08X.\n", &v_cpu::add_ALIb);
	v_cpu0.test_instr(&instr);

	bit8u *test_addr = avm_mem.get_vector(0x100000);
	printf("The host address of physical address of client 0x100000 is: %08X.\n", (bit32u)test_addr);

	bit32u disp = 0x12345678;
	bit32u disp1;
	avm_mem.write_physical_page(&v_cpu0, 0x100000, 4, &disp);
	avm_mem.read_physical_page(&v_cpu0, 0x100000, 4, &disp1);
	printf("disp1=%08X.\n", disp1);

	v_cpu0.cr0.set_PG(1);
	v_cpu0.TLB_init();
	//paging_test();

	v_cpu0.fetch_decode(test_aaa, &instr, 15);
	v_cpu0.test_instr(&instr);
	return 0;
}

#define GUESTOS_MEM 0x10000

void paging_test() {
	unsigned masked = PAGE_SIZE - 1;

	unsigned pte_entries = ((GUESTOS_MEM % PAGE_SIZE) == 0) ?
							(GUESTOS_MEM / PAGE_SIZE) :
							((GUESTOS_MEM/ PAGE_SIZE) + 1);
	unsigned pte_mem = pte_entries * 4;

	unsigned pde_entries = ((pte_mem % PAGE_SIZE) == 0) ?
							(pte_mem / PAGE_SIZE) :
							((pte_mem / PAGE_SIZE) + 1);
	unsigned pde_mem = pde_entries * 4;

	bit8u *pde_phy_addr = new bit8u[pde_mem + PAGE_SIZE];
	memset(pde_phy_addr, pde_mem + PAGE_SIZE, 0);
	bit8u *pde_phy_addr_aligned = (bit8u *)((unsigned)(pde_phy_addr + masked) & (~masked));
	bit32u *pde_phy_addr_copy = (bit32u *)pde_phy_addr_aligned;

	bit8u *pte_phy_addr = new bit8u[pte_mem + PAGE_SIZE];
	memset(pte_phy_addr, pte_mem + PAGE_SIZE, 0);
	bit8u *pte_phy_addr_aligned = (bit8u *)((unsigned)(pte_phy_addr + masked) & (~masked));
	bit32u *pte_phy_addr_copy = (bit32u *)pte_phy_addr_aligned;

	avm_phy_addr pde_tmp = 0x1000;
	avm_phy_addr pte_tmp = 0x5000;

	v_cpu0.setCR3((unsigned)pde_phy_addr_aligned);
#ifdef DEBUG
	printf("CR3 = %08X\n", v_cpu0.getCR3());
	printf("%08X pde entries initialized.\n", pde_entries);
	printf("%08X pte entries initialized.\n", pte_entries);
#endif

	// Initialize PDE
	for (int i = 0; i < pde_entries; i++) {
		*pde_phy_addr_copy = (bit32u)pde_tmp | RW_FLAG | P_FLAG;
		pde_phy_addr_copy += 1;
		pde_tmp += PAGE_SIZE;	
#ifdef DEBUG
		printf("PDE%01d - Base of PTE:%08X.\n", i, (pde_tmp - PAGE_SIZE));
#endif
	}

	// Initialize PTE
	for (int j = 0; j < pte_entries; j++) {
		*pte_phy_addr_copy = (bit32u)pte_tmp | RW_FLAG | P_FLAG;
		pte_phy_addr_copy += 1;
		pte_tmp += PAGE_SIZE;
#ifdef DEBUG
		printf("	PTE%d: %08X.\n", j, *(pte_phy_addr_copy - 1));
#endif
	}

	bit8u *buffer = new bit8u[16];
	bit8u *buffer1 = new bit8u[16];

	for (int n = 0; n < 16; n++) {
		buffer[n] = n * 2;
	}
/* Testing phrase 1
 * Copy PDE and PTE to clientOS physical memory.
 * avm_hostpage_addr -> avm_phy_addr. 
 * */
	v_cpu0.access_write_physical(0x0, pde_mem, pde_phy_addr_aligned);	
	v_cpu0.access_write_physical(0x1000, pte_mem, pte_phy_addr_aligned);
/* Testing phrase 2
 * Access linear address of vcpu.
 * avm_addr -> avm_phy_addr
 * */
	v_cpu0.access_write_linear(0x0, 16, 0, AVM_RW, buffer);
	v_cpu0.access_read_linear(0x0, 16, 0, AVM_RW, buffer1);
	for (int m = 0; m < 16; m++) {
		printf("buffer1[%d]: %02X.\n", m, buffer1[m]);
	}
	
	return;
}
